home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************/
- /* */
- /* Minimal Function, All C interrupt serial routine */
- /* */
- /* Supports buffered input, output */
- /* */
- /* */
- /************************************************************************/
- /* */
- /* This code is donated to the public domain */
- /* */
- /************************************************************************/
-
- #include <dos.h>
- #include <conio.h>
- #include <stdlib.h>
-
- /************************************************************************/
- /* */
- /* The following callable subroutines are in this library */
- /* */
- /************************************************************************/
- int ser_int_icount(); /* Count of chars in input buffer */
- int ser_int_ocount(); /* Count of chars in output buffer */
- int ser_int_ofree(); /* Count of free space in output buffer */
- void ser_int_iflush(); /* Flushes input buffer */
- void ser_int_init(); /* Initializes interrupt handler */
- void ser_int_term(); /* Terminates interrupt handler */
- void ser_int_putc(); /* Sends a character */
- int ser_int_getc(); /* Gets a character */
- void ser_int_puts(); /* Sends a string */
- void ser_int_break(); /* Sends a break signal */
-
- /************************************************************************/
- /* Definitions of the 8250's registers */
- /************************************************************************/
-
- unsigned ser_vect; /* Interrupt vector to use */
- unsigned DataPort; /* Data register */
- unsigned IER; /* Interrupt enable register */
- unsigned IIR; /* Interrupt ID register */
- unsigned LCR; /* Line control register */
- unsigned MControl; /* Modem control register */
- unsigned LStatus; /* Line Status */
- unsigned MStatus; /* Modem status */
-
- unsigned IntControl; /* For 8259 interrupt controller */
- unsigned EnableIRQ4; /* Enable interrupt 4 */
- unsigned DisableIRQ4; /* Disable interrupt 4 */
- unsigned RS8259;
- unsigned EOI;
- unsigned TxReady; /* Transmit buffer empty */
-
- static (_interrupt _far *old_vect)();
- /* The previous interrupt vector. */
-
- /************************************************************************/
- /* Definitions of the interrupt enable register */
- /************************************************************************/
-
- #define IER_RX 0x01 /* Enable Data Ready interrupts */
- #define IER_TX 0x02 /* Enable Transmit Empty Interrupts */
- #define IER_LS 0x04 /* Enable line status interrupts */
- #define IER_MS 0x08 /* Enable modem status interrupts */
-
- /************************************************************************/
- /* Definitions of the interrupt ID register */
- /************************************************************************/
- #define IIR_IPending 0x01 /* Interrupt is pending */
- #define IIR_Mask 0x06 /* Mask to get Interrupt type */
- #define IIR_MS 0x00 /* Modem status interrupt */
- #define IIR_TX 0x02 /* Transmit empty interrupt */
- #define IIR_RX 0x04 /* Data available interrupt */
- #define IIR_LS 0x06 /* Line status interrupt */
-
- #define BUFSIZE 512 /* How many characters in the buffer? */
- #define XOFF_SIZE 384 /* How full do we get before XOFFing */
- #define XON_SIZE 128 /* How empty to we get before XONing */
- #define XOFF 19 /* value of the XOFF character */
- #define XON 17 /* value of the XON character */
-
- /************************************************************************/
- /* */
- /* Definitions for the input buffer. */
- /* */
- /* We make it a 'far' array so as not to eat up valuable data */
- /* space in the small and medium models. */
- /* */
- /* The buffers are simple circular buffers, where there is */
- /* one pointer to fill the buffer and another to empty it. */
- /* A count variable keeps track of the size of the buffer */
- /* (although it could be determined by computing */
- /* (fill-empty)%BUFSIZE). */
- /* */
- /************************************************************************/
-
- static unsigned char far in_buf[BUFSIZE];
- static int in_fill =0; /* Where to place the next char */
- static int in_empty =0; /* Where to get the next char */
- static int in_count =0; /* How much data is in buffer */
- static int xoff_sent =0; /* Did we send an XOFF? */
-
- /************************************************************************/
- /* */
- /* Definitions for the output buffer */
- /* */
- /************************************************************************/
-
- static unsigned char far out_buf[BUFSIZE];
- static int out_fill =0;
- static int out_empty =0;
- static int out_count =0;
-
- static int tx_off =1; /* These means the interrupts */
- /* for the transmitter are now */
- /* off (since we have nothing */
- /* to send right now). */
-
- /************************************************************************/
- /* */
- /* ser_int_icount: Return the number of bytes in the input */
- /* buffer. 0 means no data available. */
- /* */
- /************************************************************************/
- int ser_int_icount()
- {
- return(in_count);
- }
-
- /************************************************************************/
- /* */
- /* ser_int_ocount: Returns the number of bytes in the output */
- /* buffer. This information is useful if you */
- /* are uploading a large block of data and do */
- /* not want to hand this library data quicker */
- /* than it can be transmitted. E.g., */
- /* */
- /* while (more data) */
- /* { */
- /* while (ser_int_ocount()) */
- /* ; */
- /* send_data(); */
- /* } */
- /* */
- /************************************************************************/
- int ser_int_ocount()
- {
- return(out_count);
- }
-
- /************************************************************************/
- /* */
- /* ser_int_ofree: Returns the amount of free space left in */
- /* the output buffer. Useful in preventing */
- /* hanging in ser_int_putc while sending a */
- /* a large block of data. E.g., */
- /* */
- /* while (more_data) */
- /* { */
- /* while (ser_int_ofree() == 0) */
- /* do_something_else(); */
- /* ser_int_putc(data); */
- /* } */
- /* */
- /************************************************************************/
- int ser_int_ofree()
- {
- return(BUFSIZE-out_count);
- }
-
- /************************************************************************/
- /* */
- /* ser_int_iflush: Throw away any data in the input buffer. */
- /* Note that if a character arrives just as */
- /* this subroutine is returning, there will */
- /* be data in the input buffer upon return. */
- /* Thus, you cannot rely upon the buffer */
- /* being empty (for whatever reason). */
- /* */
- /************************************************************************/
- void ser_int_iflush()
- {
- _disable();
-
- /*****************************************/
- in_fill = 0;
- in_empty = 0;
- in_count = 0;
- /*****************************************/
-
- _enable();
- }
-
- /************************************************************************/
- /* */
- /* int_handler: This routine catches the serial port */
- /* interrupts, and performs accordingly. */
- /* */
- /************************************************************************/
- static void interrupt far int_handler()
- {
- int ax;
- static int send_now = 0;
-
- /* While there is more work to do... */
- while (0 == ((ax = inp(IIR)) & IIR_IPending))
- {
- switch (ax & IIR_Mask)
- {
- case IIR_RX: /* New data has arrived */
-
- ax =inp(DataPort);
-
- if (in_count != BUFSIZE)
- {
- in_buf[in_fill] = (unsigned char) ax;
- in_fill = (in_fill+1) & (BUFSIZE-1);
- in_count++;
-
- /* Check for the buffer filling up... */
- if ((in_count == XOFF_SIZE) && (!xoff_sent))
- {
- if (tx_off)
- {
- outp(DataPort,XOFF);
- tx_off = 0;
- }
- else
- {
- send_now = XOFF;
- }
- xoff_sent = 1;
- }
- }
- break;
-
- case IIR_TX: /* Ready to transmit another */
-
- if (send_now)
- {
- outp(DataPort,send_now);
- send_now = 0;
- }
- else if (out_count)
- {
- outp(DataPort,out_buf[out_empty]);
- out_empty = (out_empty+1) & (BUFSIZE-1);
- out_count--;
- }
- else
- {
- /* Since we aren't feeding it another */
- /* byte, it won't interrupt again */
- tx_off = 1;
- }
- break;
-
- default:
- break;
- }
- }
- outp(RS8259,EOI);
- }
-
-
- /************************************************************************/
- /* */
- /* ser_int_init: Initialize the serial port handler. */
- /* Note that if you change the baud rate */
- /* via the ROM bios (or MSC's _bios* funcs) */
- /* for example, you need to call this routine */
- /* again to restart the interrupts. */
- /* */
- /* The argument, com_port, should be either 1 or 2 */
- /************************************************************************/
- static int been_inited = 0;
- void ser_int_term(void);
-
- void ser_int_init(int com_port)
- {
- register int ax;
- register int i;
-
- if (been_inited)
- {
- ser_int_term();
- }
- else
- {
- onexit(ser_int_term);
- been_inited = 1;
- }
-
- if (com_port == 2)
- {
- ser_vect =0xb;
- DataPort =0x2f8;
- IER =0x2f9;
- IIR =0x2fa;
- LCR =0x2fb;
- MControl =0x2fc;
- LStatus =0x2fd;
- MStatus =0x2fe;
-
- IntControl =0x21;
- EnableIRQ4 =0xF7;
- DisableIRQ4 =0x08;
- RS8259 =0x20;
- EOI =0x20;
- TxReady =0x20;
- }
- else /* COM 1 */
- {
- ser_vect =0xc;
- DataPort =0x3f8;
- IER =0x3f9;
- IIR =0x3fa;
- LCR =0x3fb;
- MControl =0x3fc;
- LStatus =0x3fd;
- MStatus =0x3fe;
-
- IntControl =0x21;
- EnableIRQ4 =0xef;
- DisableIRQ4 =0x10;
- RS8259 =0x20;
- EOI =0x20;
- TxReady =0x20;
- }
-
- _disable();
-
- old_vect = _dos_getvect(ser_vect);
-
- _dos_setvect(ser_vect,int_handler);
-
- _disable();
-
- /*****************************************/
- ax =inp(IntControl);
- ax =ax & EnableIRQ4;
- outp(IntControl,ax);
-
- outp(IER,IER_RX | IER_TX);
-
- for (i=0;i<6;i++)
- inp(DataPort+i);
-
- ax =inp(LCR);
- ax =ax & 0x3f;
- outp(LCR,ax);
-
- outp(MControl,0xb);
-
- outp(RS8259,EOI);
- /*****************************************/
-
- _enable();
- }
-
- /************************************************************************/
- /* */
- /* ser_int_term: Turn off the interrrupts to make the */
- /* world safe for us to exit... */
- /* */
- /************************************************************************/
- void ser_int_term()
- {
- #if 0
- /* Wait for all data in the buffer to be transmitted */
- while (out_count)
- {}
-
- /* Wait for the UART to drain out */
- while ((inp(LStatus) & 0x60) != 0x60)
- {}
- #endif
-
- _disable();
- /*****************************************/
-
- outp(IntControl,inp(IntControl) | DisableIRQ4);
- outp(LCR,inp(LCR) & 0x7f);
- outp(IER,0);
- outp(MControl,0);
-
- /*****************************************/
- _enable();
-
- _dos_setvect(ser_vect,old_vect);
- }
-
- /************************************************************************/
- /* */
- /* ser_int_putc: Transmit a character out the UART */
- /* */
- /************************************************************************/
- void ser_int_putc(int c)
- {
- _disable();
-
- /*****************************************/
-
- /* If there is nothing ahead of us, just send it! */
- if (tx_off)
- {
- outp(DataPort,c);
- tx_off = 0;
- }
- else /* Put character in the buffer */
- {
- if (out_count == BUFSIZE)
- {
- _enable();
- /*****************************************/
- /* Buffer is full, wait for space */
- while (out_count == BUFSIZE)
- ;
- /*****************************************/
- _disable();
- }
- out_buf[out_fill] = (unsigned char) c;
- out_fill = (out_fill + 1) & (BUFSIZE-1);
- out_count++;
- }
- /*****************************************/
-
- _enable();
- }
-
-
- /************************************************************************/
- /* */
- /* ser_int_getc: Get a character from the input buffer. */
- /* Returns character or -1 if no data available */
- /* */
- /************************************************************************/
- int ser_int_getc()
- {
- int b;
-
- if (in_count == 0)
- return(-1);
-
- b =in_buf[in_empty];
- in_empty = (in_empty+1) & (BUFSIZE-1);
-
- _disable();
-
- /*****************************************/
- in_count--;
- /*****************************************/
-
- _enable();
-
- if ((in_count == XON_SIZE) && xoff_sent)
- {
- ser_int_putc(XON);
- xoff_sent = 0;
- }
- return(b);
- }
-
- /************************************************************************/
- /* */
- /* ser_int_puts: Transmit a string of characters */
- /* */
- /************************************************************************/
- void ser_int_puts(char *s)
- {
- while (*s)
- ser_int_putc(*s++);
- }
-
-
- /************************************************************************/
- /* */
- /* ser_int_break: Raise BREAK for 200 ms, then turn it off */
- /* for 200 ms. */
- /* */
- /* This routine is structured so that you can call it repeatedly */
- /* and the other end will sense distinct break signals rather */
- /* than one very long one. */
- /* */
- /************************************************************************/
- void ser_int_break()
- {
- int ax;
- unsigned long far *ticks;
-
- unsigned long start, cur;
-
- FP_SEG(ticks) = 0x40;
- FP_OFF(ticks) = 0x6c;
-
- start =*ticks;
-
- _disable();
-
- /*****************************************/
- /* Turn on BREAK signal */
- /*****************************************/
- ax =inp(LCR);
- ax |= 0x40;
- outp(LCR,ax);
- _enable();
-
- /* Wait for 200 ms */
- while (1)
- {
- cur =*ticks;
- if ((cur-start) > 4) /* 220 uSecs */
- break;
- }
-
- _disable();
- /*****************************************/
- /* Turn off BREAK signal */
- /*****************************************/
- ax =inp(LCR);
- ax &= ~0x40;
- outp(LCR,ax);
- _enable();
-
- start =*ticks;
-
- while (1)
- {
- cur =*ticks;
- if ((cur-start) > 4)
- break;
- }
- }
-